/************************************************************************
filename:   CEGUISlotFunctorBase.h
created:    Tue Feb 28 2006
author:     Paul D Turner <paul@cegui.org.uk>
*************************************************************************/

#ifndef _HgeGuiEventSlot_h_
#define _HgeGuiEventSlot_h_

// forward refs
class EventArgs;
class Event;

/*!
\brief
Defines abstract interface which will be used when constructing various
functor objects that bind slots to signals (or in CEGUI terms, handlers to
events).
*/
class SlotFunctorBase
{
public:
	virtual ~SlotFunctorBase() {};
	virtual bool operator()(const EventArgs& args) = 0;
};

/*!
\brief
Slot functor class that calls back via a free function pointer.
*/
class FreeFunctionSlot : public SlotFunctorBase
{
public:
	//! Slot function type.
	typedef bool (SlotFunction)(const EventArgs&);

	FreeFunctionSlot(SlotFunction* func) : d_function(func) {}

	virtual bool operator()(const EventArgs& args)
	{
		return d_function(args);
	}

private:
	SlotFunction* d_function;
};

/*!
\brief
Slot template class that creates a functor that calls back via a class
member function.
*/
template<typename T> class MemberFunctionSlot : public SlotFunctorBase
{
public:
	//! Member function slot type.
	typedef bool(T::*MemberFunctionType)(const EventArgs&);

	MemberFunctionSlot(MemberFunctionType func, T* obj) : d_function(func),	d_object(obj) {}

	virtual bool operator()(const EventArgs& args)
	{
		return (d_object->*d_function)(args);
	}

private:
	MemberFunctionType d_function;
	T* d_object;
};

/*!
\brief
Slot template class that creates a functor that calls back via a functor
object reference.
*/
template<typename T> class FunctorReferenceSlot : public SlotFunctorBase
{
public:
	FunctorReferenceSlot(T& functor) : d_functor(functor) {}
	
	virtual bool operator()(const EventArgs& args)
	{
		return d_functor(args);
	}

private:
	T& d_functor;
};

/*!
\brief
Slot template class that creates a functor that calls back via a functor
object pointer.
*/
template<typename T> class FunctorPointerSlot : public SlotFunctorBase
{
public:
	FunctorPointerSlot(T* functor) : d_functor(functor) {}
	
	virtual bool operator()(const EventArgs& args)
	{
		return (*d_functor)(args);
	}

private:
	T* d_functor;
};

/*!
\brief
Slot template class that creates a functor that calls back via a copy of a
functor object.
*/
template<typename T> class FunctorCopySlot : public SlotFunctorBase
{
public:
	FunctorCopySlot(const T& functor) : d_functor(functor) {}
	
	virtual bool operator()(const EventArgs& args)
	{
		return d_functor(args);
	}

private:
	T d_functor;
};

/*!
\brief
SubscriberSlot class which is used when subscribing to events.

For many uses, the construction of the SubscriberSlot may be implicit, so
you do not have to specify Subscriber in your subscription calls. Notable
exceptions are for subscribing member functions and references to functor
objects.
*/
class SubscriberSlot
{
public:
	/*!
	\brief
	Default constructor.  Creates a SubscriberSlot with no bound slot.
	*/
	SubscriberSlot();

	/*!
	\brief
	Creates a SubscriberSlot that is bound to a free function.
	*/
	SubscriberSlot(FreeFunctionSlot::SlotFunction* func);

	/*!
	\brief
	Destructor.  Note this is non-virtual, which should be telling you not
	to sub-class!
	*/
	~SubscriberSlot();

	/*!
	\brief
	Invokes the slot functor that is bound to this Subscriber.  Returns
	whatever the slot returns, unless there is not slot bound when false is
	always returned.
	*/
	bool operator()(const EventArgs& args) const
	{
		return (*d_functor_impl)(args);
	}

	/*!
	\brief
	Returns whether the SubscriberSlot is internally connected (bound).
	*/
	bool connected() const
	{
		return d_functor_impl != 0;
	}

	/*!
	\brief
	Disconnects the slot internally and performs any required cleanup
	operations.
	*/
	void cleanup();

	// templatised constructors
	/*!
	\brief
	Creates a SubscriberSlot that is bound to a member function.
	*/
	template<typename T>
	SubscriberSlot(bool (T::*function)(const EventArgs&), T* obj) :
	d_functor_impl(new MemberFunctionSlot<T>(function, obj))
	{}

	/*!
	\brief
	Creates a SubscriberSlot that is bound to a copy of a functor object.
	*/
	template<typename T>
	SubscriberSlot(const T& functor) : d_functor_impl(new FunctorCopySlot<T>(functor)) {}

	/*!
	\brief
	Creates a SubscriberSlot that is bound to a functor pointer.
	*/
	template<typename T>
	SubscriberSlot(T* functor) :
	d_functor_impl(new FunctorPointerSlot<T>(functor))
	{}

private:
	//! Points to the internal functor object to which we are bound
	SlotFunctorBase* d_functor_impl;
};

/*!
\brief
Class that tracks a SubscriberSlot, its group, and the Event to which it was
subscribed.  This is effectively what gets returned from the calls to the
Event::subscribe members, though BoundSlot is always wrapped in a
reference counted pointer.  When a BoundSlot is deleted, the connection is
unsubscribed and the SubscriberSlot is deleted.
*/
class BoundSlot
{
public:
	typedef unsigned int Group;

	/*!
	\brief
	Constructor.

	\param group
	The subscriber group this slot is attached to.

	\param subscriber
	The actual slot object that is controlling this connection binding.

	\param event
	The Event object to which the subscribed slot is attached.
	*/
	BoundSlot(Group group, const SubscriberSlot& subscriber, Event& event);

	/*!
	\brief
	Copy constructor.
	*/
	BoundSlot(const BoundSlot& other);

	/*!
	\brief
	Destructor
	*/
	~BoundSlot();

	/*!
	\brief
	Returns whether the slot which this object is tracking is still
	internally connected to the signal / event mechanism.

	\return
	- true to indicate that the slot is still connected.
	- false to indicate that the slot has been disconnected.
	*/
	bool connected() const;

	/*!
	\brief
	Disconnects the slot.  Once disconnected, the slot will no longer be
	called when the associated signal / event fires.  There is no way to
	re-connect a slot once it has been disconnected, a new subscription to
	the signal / event is required.

	\return
	Nothing.
	*/
	void disconnect();

	/*!
	\brief
	Equality operator

	\param other
	The BoundSlot to compare against.

	\return
	- true if the BoundSlot objects represent the same connection.
	- false if the BoundSlot objects represent different connections.
	*/
	bool operator==(const BoundSlot& other) const;

	/*!
	\brief
	Non-equality operator

	\param other
	The BoundSlot to compare against.

	\return
	- true if the BoundSlot objects represent different connections.
	- false if the BoundSlot objects represent the same connection.
	*/
	bool operator!=(const BoundSlot& other) const;

private:
	friend class Event;
	// no assignment.
	BoundSlot& operator=(const BoundSlot& other);
	Group d_group;                  //! The group the slot subscription used.
	SubscriberSlot* d_subscriber;   //! The actual slot object.
	Event* d_event;                 //! The event to which the slot was attached
};

#endif  // end of guard _HgeGuiEventSlot_h_